/*
 *	Paralation Lisp
 *
 *	Author:	S.C.Merrall
 *
 *	File:	mp_xnet.m
 *
 *	Contents:	mp_xnet
 *			get
 *			edge
 *
 *	Description:	The functions in this file were created
 *                      explicitly to make handling wrapped nearest
 *                      neighbour communication easier. The most
 *                      important difference is that teh functions do
 *                      not work on single offsets but a list of
 *                      offsets which is transferred between the
 *                      scratch spaces of the host and ACU
 *
 *	Change History:
 *
 *	Date   Name Comment
 *    -------- ---- -------
 *    05:06:92 SCM  Created
 *    17:06:92 SCM  Added edge, which is a context stack modifier
 */

#include <mpl.h>
#include <stdio.h>
#include "proc_pair.h"

#include "mp_eubang.h"  /*  Includes constant.h too */
#include "mp_object.h"
#include "mp_debug_off.h"
#include "mp_mem_mgmt.h"
#include "mp_gc.h"
#include "mp_utils.h"

/*----------------------------------------------------------------------------*
 * Function   : get
 *
 * Parameters : MPC:                            The context of the plural
 *              MP_PluralHeap MPPH_data:	The plural to be shifted in 
 *						the given direction
 *		int direction:			The direction to shift in
 *
 * Description: Encodes the lisp objects into the scratch space shifts them
 *		in the specified direction builds copies in the pes and 
 *		the value of the argument is now the shifted structures
 *
 * Result     : int FAIL/SUCCESS
 *---------------------------------------------------------------------------*/

#ifdef __STDC__

int get( object MPC, MP_PluralHeap MPPH_data, int direction )

#else

int get( MPC, MPPH_data, direction )

object MPC;
MP_PluralHeap MPPH_data;
int direction;

#endif

{
  int delta_x = 0;
  int delta_y = 0;
  plural int other_edge;
  MP_PluralHeap MPPH_pluralHeap;
  plural int index;
DBG_CALL("get");
DBG_ARGS(fprintf(dbg,"MPC=%x,",MPC);DBG_PARG("MPPH_data","%x ",MPPH_data);fprintf(dbg,"direction=%d",direction));
  

  OM_with_context(MPC) {

    scratch[0] = 1;
    encode(MPPH_data);

    switch (direction) {

     case MP_NORTH: delta_y = -1; break;
     case MP_SOUTH: delta_y =  1; break;
     case MP_WEST : delta_x =  2; break;
     case MP_EAST : delta_x = -2; break;

    }
DEBUG(fprintf(dbg,"delta_x=%d,delta_y=%d",delta_x,delta_y));

    pp_xsend(delta_y,delta_x, (plural char *plural) scratch,
	     (plural char *plural) scratch, SCRATCH_MEMORY_SIZE);

    if ((((direction == MP_NORTH) || (direction == MP_SOUTH)) && 
	 (OA_height(MPC) == PP_nyproc)) ||
	(((direction == MP_WEST) || (direction == MP_EAST)) &&
	 (OA_width(MPC) == PP_nxproc))) {
    }
    else if (((direction==MP_NORTH) && OM_top_edge(MPC)) ||
	     ((direction==MP_SOUTH) && OM_bottom_edge(MPC)) ||
	     ((direction==MP_WEST) &&  OM_left_edge(MPC)) ||
	     ((direction==MP_EAST) && OM_right_edge(MPC))) {
      
DEBUG(DBG_PARG("iproc"," %d",iproc));
      other_edge = ((iyproc - (delta_y * OA_height(MPC))) * nxproc) +
        ((ixproc + (OA_width(MPC) * delta_x)) % nxproc);

      other_edge = (other_edge + nproc ) % nproc;
DEBUG(DBG_PARG("other_edge"," %d",other_edge));
	
      ss_rfetch(other_edge,scratch,scratch,SCRATCH_MEMORY_SIZE);
    }
    
    index = 1;
    if (decode(MPPH_data, &index)== FAIL) {
      
DBG_FAIL(fprintf(dbg,"FAIL: decode stage of get failed"));
      mp_error = MP_DECODE_IN_GET_FAILED;
      return FAIL;
    }
  } /* matches POM_with_conttext(MPC) */

DBG_EXIT(fprintf(dbg,"SUCCESS"));
  return SUCCESS;
}

/*----------------------------------------------------------------------------*
 * Function   : mp_xnet
 *
 * Parameters : int direction:	Indicating north, south, east or west
 *		int no_of_ofsts:Number of offsets
 *		int *fe_address:Where the offset list is on the host
 *
 * Description: The list of offsets form a strip in the appropriate dimension
 *		of a tiled virtual processor set, we want to shift them in
 *		desired direction carrying the overflow between the tiles 
 *		with toroidal wrap around.
 *
 * Result     : int:	FAIL/SUCCESS
 *---------------------------------------------------------------------------*/

#ifdef __STDC__

visible int mp_xnet(int direction,int no_of_offsets,char *fe_address )

#else

visible int mp_xnet( direction, no_of_offsets, fe_address )

int direction;
int no_of_offsets;
char *fe_address;

#endif

{
  natural first_ofst = 0;
  int i, delta_x, delta_y;
  natural *natural_scratch = (natural *) acu_scratch;
  plural natural tmp, gws;
  MP_PluralHeap MPPH_tmp;
  int from_iproc, to_iproc;
  object MPC;
  plural int index;
  
DBG_CALL("mp_xnet");
DBG_ARGS(fprintf(dbg,"direction=%d, no_of_ofsts=%d, fe_address=%x",direction,no_of_offsets,fe_address));

  if (copyIn(fe_address, acu_scratch, SCRATCH_MEMORY_SIZE) != 0) {

    mp_error = MP_COPY_IN_FAILED;
DBG_FAIL(fprintf(dbg,"FAIL: Error paging in offset list"));      
    return FAIL;
  }

  PP_on_set() {

    /* do all the shifts */

    for (i=0; i< no_of_offsets; i++) {

      MPC = (object) natural_scratch[(i*2)];
      MPP_2_MPPH( MPPH_tmp, natural_scratch[(i*2)+1]);
      if (get(MPC, MPPH_tmp, direction) == FAIL) {

DBG_FAIL(fprintf(dbg,"FAIL: get failed for %dth plural", i));
	return FAIL;
      }
    }
      
    /*  now handle edge condition thingies, we expect most of the contexts
     *  to be the same so we set the activity to the edge of it to start off
     */

   MPPH_tmp = &gws;

    MPC = (object) natural_scratch[0];
    OM_with_context(MPC) {
    
      if (((direction==MP_NORTH) && OM_top_edge(MPC)) ||
	  ((direction==MP_SOUTH) && OM_bottom_edge(MPC)) ||
	  ((direction==MP_WEST) &&  OM_left_edge(MPC)) ||
	  ((direction==MP_EAST) && OM_right_edge(MPC))) {

	gws = plural_memory[natural_scratch[0+1]];
    
	for (i=1; i<no_of_offsets; i++) {
	
	  if (MPC == (object) natural_scratch[2*i]) {
	    tmp = plural_memory[natural_scratch[(2*i)+1]];
	    plural_memory[natural_scratch[(2*i)+1]] = gws;
	    gws = tmp;
	    MPC =  (object) natural_scratch[2*i];
	  }
	  else { /* This is the last context */

	    from_iproc = selectFirst();
	    scratch[0] = 1;
DEBUG(DBG_PARG("iproc","%d ",iproc));
DEBUG(DBG_PARG("scratch[0-3]","%08x ",*((plural int *) scratch)));
DEBUG(DBG_PARG("type","%d ",OA_info(MPPH_tmp)));
	    encode(MPPH_tmp);
DEBUG(DBG_PARG("scratch[0-3]","%08x ",*((plural int *) scratch)));
DEBUG(DBG_PARG("scratch[4-7]","%08x ",*(((plural int *) scratch)+1)));
	    all {
	      PP_on_set() {
	      MPC =  (object) natural_scratch[2*i];
	      OM_with_context(MPC) {

		if (((direction==MP_NORTH) && OM_top_edge(MPC)) ||
		    ((direction==MP_SOUTH) && OM_bottom_edge(MPC)) ||
		    ((direction==MP_WEST) &&  OM_left_edge(MPC)) ||
		    ((direction==MP_EAST) && OM_right_edge(MPC))) {
	      
DEBUG(DBG_PARG("iproc","%d ",iproc));

		  to_iproc = selectFirst();
DEBUG(fprintf(dbg,"from_iproc=%d, to_iproc=%d",from_iproc,to_iproc));
                  delta_y = -((from_iproc/nxproc)-(to_iproc/nxproc));
                  delta_x = (from_iproc-to_iproc)%nxproc;
DEBUG(fprintf(dbg,"delta_x=%d, delta_y=%d",delta_x, delta_y));
		  ss_xfetch(delta_y,delta_x,scratch,scratch,
			    SCRATCH_MEMORY_SIZE);
DEBUG(DBG_PARG("scratch[0-3]","%08x ",*((plural int *) scratch)));
DEBUG(DBG_PARG("scratch[4-7]","%08x ",*(((plural int *) scratch)+1)));
		  index = 1;
DEBUG(DBG_PARG("gws","%d ",gws));
		  decode(MPPH_tmp,&index);
DEBUG(DBG_PARG("gws","%d ",gws));
DEBUG(DBG_PARG("type","%d ",OA_info(MPPH_tmp)));
DEBUG(DBG_PARG("value","%d ",*(plural int *plural)OA_data(MPPH_tmp)));
		  tmp = plural_memory[natural_scratch[(2*i)+1]];
DEBUG(DBG_PARG("tmp","%d ",tmp));
		  plural_memory[natural_scratch[(2*i)+1]] = gws;
                  gws = tmp;
		  scratch[0] = 1;
		  encode(MPPH_tmp);
		  ss_xsend(delta_y,delta_x,scratch,scratch,
			   SCRATCH_MEMORY_SIZE);
		}
	      }
  	      }
	    }
DEBUG(DBG_PARG("iproc","%d ",iproc));
	      index = 1;
	      decode(MPPH_tmp,&index);
	  }
	}
	plural_memory[natural_scratch[0+1]] = gws;
      }
    }
  }
  DBG_EXIT(fprintf(dbg,"SUCCESS"));
  return SUCCESS;
}

  
/*----------------------------------------------------------------------------*
 * Function   : edge
 *
 * Parameters : object MPC:	The MasPar context whose contexts stacks we
 *				are going to munge.
 *		int direction:	The edge we want active
 *
 * Description: Context stack modifier in the same way as the foc functions
 *		(perhaps it should be in that module) so that the indicated
 *		edge of the rectangular context is active
 *
 * Result     : int:	
 *---------------------------------------------------------------------------*/

#ifdef __STDC__

visible int mp_edge( object MPC, int direction )

#else

visible int mp_edge( MPC, direction )

object MPC;
int direction;

#endif

{
  int result;
  plural natural old_context;
  plural natural *plural new_context;
  MP_PluralHeap MPPH_context_stack;
DBG_CALL("mp_edge");
DBG_ARGS(fprintf(dbg,"MPC=%x, direction=%d",MPC,direction));

  OM_with_context(MPC) {

    MPP_2_MPPH(MPPH_context_stack, OA_offset(MPC));
    old_context = * (plural natural *plural) OA_data(MPPH_context_stack);

    if (cons(MPPH_context_stack, MPPH_context_stack,
	     MPPH_context_stack)==FAIL) {

DBG_FAIL(fprintf(dbg,"FAIL: Unable to cons up context stack"));
      return FAIL;
    }

    new_context = (plural natural *plural) OA_data(MPPH_context_stack);
    *new_context = NIL;
    if (old_context != NIL) {

      if (((direction == MP_NORTH) && OM_top_edge(MPC)) ||
	  ((direction == MP_SOUTH) && OM_bottom_edge(MPC)) ||
	  ((direction == MP_EAST) && OM_right_edge(MPC)) ||
	  ((direction == MP_WEST) && OM_left_edge(MPC))) {

	*new_context = NOT_NIL;
      }
    }

    if(globalor(*new_context != NIL) == 0) result = MP_NONE_ACTIVE;
    else result = MP_SOME_ACTIVE;
  }

DBG_EXIT(fprintf(dbg,"SUCCESS: %d",result));
  return result;
}

      
